package com.example.flower_shop.service.usr.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.flower_shop.common.CacheObject;
import com.example.flower_shop.common.PageResult;
import com.example.flower_shop.constans.ErrorMsg;
import com.example.flower_shop.constans.PreOrderType;
import com.example.flower_shop.entity.*;
import com.example.flower_shop.enums.OrderStatus;
import com.example.flower_shop.enums.PayStatus;
import com.example.flower_shop.enums.PayType;
import com.example.flower_shop.enums.RefundStatus;
import com.example.flower_shop.exception.exception;
import com.example.flower_shop.mapper.*;
import com.example.flower_shop.service.usr.StoreOrderService;
import com.example.flower_shop.utils.CacheUtil;
import com.example.flower_shop.utils.CommonUtil;
import com.example.flower_shop.utils.DecimalUtil;
import com.example.flower_shop.vo.order.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.BatchResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author zbxComputer
 * @description 针对表【sys_store_order(订单表)】的数据库操作Service实现
 * @createDate 2024-11-21 12:49:00
 */
@Service
@Slf4j
public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOrder>
        implements StoreOrderService {
    private static final ReentrantLock lock = new ReentrantLock();
    @Resource
    private UserMapper userMapper;
    @Resource
    private StoreOrderMapper orderMapper;
    @Resource
    private StoreProductMapper productMapper;
    @Resource
    private StoreOrderInfoMapper orderInfoMapper;
    @Resource
    private StoreCartMapper storeCartMapper;
    @Resource
    private TransactionTemplate transactionTemplate;

    @Override
    public void takeOrder(Long orderId) {
        LambdaQueryWrapper<StoreOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StoreOrder::getId, orderId)
                .eq(StoreOrder::getUid, StpUtil.getLoginIdAsLong())
                .eq(StoreOrder::getPaid, PayStatus.PAID.getCode())
                .eq(StoreOrder::getStatus, OrderStatus.WAITING_TO_RECEIVE.getCode());
        StoreOrder storeOrder = getOne(queryWrapper);
        if (storeOrder == null) {
            throw new exception("订单未发货");
        }
        boolean update = orderMapper.updateOrderStatus(orderId, OrderStatus.WAITING_TO_RECEIVE.getCode()
                , OrderStatus.WAITING_TO_EVALUATE.getCode());
        if (!update) {
            throw new exception("订单状态异常");
        }
    }

    @Override
    public void cancelOrder(Long orderId) {
        LambdaQueryWrapper<StoreOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StoreOrder::getId, orderId)
                .eq(StoreOrder::getUid, StpUtil.getLoginIdAsLong())
                .eq(StoreOrder::getPaid, PayStatus.UNPAID.getCode());
        StoreOrder storeOrder = getOne(queryWrapper);
        if (storeOrder == null) {
            throw new exception("订单不存在");
        }
        if (!removeById(orderId)) {
            throw new exception("取消订单失败");
        }
    }

    @Override
    public List<AdmOrderDetailResp> getOrderDetail(Long orderId) {
        LambdaQueryWrapper<StoreOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StoreOrder::getId, orderId)
                .eq(StoreOrder::getUid, StpUtil.getLoginIdAsLong());
        StoreOrder order = orderMapper.selectOne(wrapper);
        if (order == null) {
            throw new exception("订单不存在!");
        }
        LambdaQueryWrapper<StoreOrderInfo> productsQuery = new LambdaQueryWrapper<>();
        productsQuery.eq(StoreOrderInfo::getOrderNo, order.getOrderNo());
        List<StoreOrderInfo> orderInfos = orderInfoMapper.selectList(productsQuery);
        return BeanUtil.copyToList(orderInfos, AdmOrderDetailResp.class);
    }

    @Override
    public void orderRefund(OrderRefundReq req) {
        Long orderId = req.getOrderId();
        LambdaQueryWrapper<StoreOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StoreOrder::getId, orderId)
                .eq(StoreOrder::getUid, StpUtil.getLoginIdAsLong())
                .eq(StoreOrder::getPaid, PayStatus.PAID.getCode())
                .eq(StoreOrder::getRefundStatus, RefundStatus.NOT_REFUND.getCode());
        StoreOrder storeOrder = getOne(queryWrapper);
        if (storeOrder == null) {
            throw new exception("订单不存在");
        }
        storeOrder.setRefundStatus(RefundStatus.APPEALING.getCode());
        storeOrder.setRefundPrice(storeOrder.getPayPrice());
        storeOrder.setRefundReasonTime(LocalDateTime.now());
        if (!updateById(storeOrder)) {
            throw new exception("申请退款失败");
        }
    }

    @Override
    public PreOrderResp preOrderLoad(String preOrderNo) {
        CacheObject cacheObject = CacheUtil.get(preOrderNo);
        if (cacheObject == null || StringUtils.isBlank(cacheObject.getData())
                || cacheObject.getExpire() < System.currentTimeMillis()) {
            throw new exception("预购订单已失效");
        }
        return JSONUtil.toBean(cacheObject.getData(), PreOrderResp.class);
    }

    @Override
    public PreOrderResp preOrder(PreOrderReq req) {
        // 如果判断下单方式是立即购买 还是购物车 选择不同的处理逻辑
        String orderType = req.getPreOrderType();
        long userId = StpUtil.getLoginIdAsLong();
        List<PreOrderDetail> orderDetails = req.getOrderDetails();
        if (orderDetails == null || orderDetails.isEmpty()) {
            throw new exception("订单详情不能为空");
        }
        Map<Long, Integer> productQuantity = new HashMap<>();
        switch (orderType) {
            // 立即购买必定只有一个商品
            case PreOrderType.buyNow -> {
                PreOrderDetail orderDetail = orderDetails.get(0);
                if (orderDetail.getQuantity() <= 0) {
                    throw new exception("数量必须大于0");
                }
                productQuantity.put(orderDetail.getProductId(), orderDetail.getQuantity());
            }
            // 购物车下单 可能有多个商品
            case PreOrderType.shoppingCart -> {
                // 获取这些商品的购物车id
                List<Long> cartIds = orderDetails.stream().map(PreOrderDetail::getShoppingCartId).toList();
                LambdaQueryWrapper<StoreCart> cartQuery = new LambdaQueryWrapper<>();
                cartQuery.in(StoreCart::getId, cartIds)
                        .eq(StoreCart::getUid, userId);
                List<StoreCart> storeCarts = storeCartMapper.selectList(cartQuery);
                if (storeCarts.isEmpty() || storeCarts.size() != cartIds.size()) {
                    throw new exception("购物车商品不存在");
                }
                for (StoreCart cart : storeCarts) {
                    productQuantity.put(cart.getProductId(), cart.getProductNum());
                }
            }
            default -> throw new exception("未知的预购类型");
        }
        // 获取这些商品的详细信息
        LambdaQueryWrapper<StoreProduct> productQuery = new LambdaQueryWrapper<>();
        productQuery.in(StoreProduct::getId, productQuantity.keySet())
                .eq(StoreProduct::getIsShow, 1);
        List<StoreProduct> products = productMapper.selectList(productQuery);
        if (products.isEmpty()) {
            throw new exception(ErrorMsg.PRODUCT_NOT_EXIST);
        }
        // 获取预下单页面商品信息
        List<ProductInfo> productInfos = products.stream().map(e -> {
            Integer quantity = productQuantity.get(e.getId());
            if (quantity == null) {
                throw new exception("商品信息错误");
            }
            // 计算单件商品价格
            return ProductInfo.builder()
                    .productId(e.getId())
                    .productImg(e.getImage())
                    .productName(e.getStoreName())
                    .quantity(quantity)
                    .price(e.getPrice())
                    .vipPrice(DecimalUtil.vip(e.getPrice()))
                    .build();
        }).toList();
        Boolean isVip = (Boolean) StpUtil.getExtra("isVip");
        // 原价
        AtomicReference<BigDecimal> totalPrice = new AtomicReference<>(BigDecimal.ZERO);
        // 现价
        AtomicReference<BigDecimal> curPrice = new AtomicReference<>(BigDecimal.ZERO);
        for (ProductInfo info : productInfos) {
            totalPrice.set(totalPrice.get().add(DecimalUtil.multiply(info.getPrice(), info.getQuantity())));
            curPrice.set(curPrice.get().add(DecimalUtil.multiply(isVip ? info.getVipPrice() : info.getPrice(),
                    info.getQuantity())));
        }
        String preOrderNo = CommonUtil.getRandomStr();
        PreOrderResp resp = PreOrderResp.builder()
                .productInfoList(productInfos)
                .preOrderNo(preOrderNo)
                // 商品原金额总价
                .totalPrice(totalPrice.get())
                // 原价 - 现价 就是 优惠价格
                .discountPrice(totalPrice.get().subtract(curPrice.get()))
                // 现价是优惠后的价格 - 会员优惠 就是实际价格 在加油费 就是应付金额
                .actualPay(curPrice.get())
                .build();
        // 设置预下单订单缓存 10小时过期
        CacheUtil.put(preOrderNo, JSONUtil.toJsonStr(resp), 10, TimeUnit.HOURS);
        return resp;
    }

    @Override
    public CreateOrderResp createOrder(CreateOrderReq req) {
        try {
            lock.lock();
            String preOrderNo = req.getPreOrderNo();
            CacheObject cacheObject = CacheUtil.get(preOrderNo);
            if (cacheObject == null || StringUtils.isBlank(cacheObject.getData())
                    || cacheObject.getExpire() < System.currentTimeMillis()) {
                throw new exception("预购订单已失效");
            }
            // 验证支付类型
            PayType.validate(req.getPayType());
            // 获取预购订单信息
            PreOrderResp preOrder = JSONUtil.toBean(cacheObject.getData(), PreOrderResp.class);
            long userId = StpUtil.getLoginIdAsLong();
            String orderNo = CommonUtil.getRandomStr();
            // 获取预下单商品信息
            List<ProductInfo> productInfoList = preOrder.getProductInfoList();
            AtomicInteger totalQuantity = new AtomicInteger(0);
            // 保存单个商品详细信息
            List<StoreOrderInfo> orderInfos = new ArrayList<>();
            for (ProductInfo info : productInfoList) {
                totalQuantity.set(totalQuantity.get() + info.getQuantity());
                StoreOrderInfo orderInfo = StoreOrderInfo.builder()
                        .orderNo(orderNo)
                        .productId(info.getProductId())
                        .productName(info.getProductName())
                        .image(info.getProductImg())
                        .price(info.getPrice())
                        .payNum(info.getQuantity())
                        .build();
                orderInfos.add(orderInfo);
            }
            UserEntity user = userMapper.selectById(userId);
            // 创建父订单
            StoreOrder order = StoreOrder.builder()
                    .uid(userId)
                    .orderNo(orderNo)
                    .proTotalPrice(preOrder.getTotalPrice())
                    .totalPrice(preOrder.getTotalPrice())
                    .payPrice(preOrder.getActualPay())
                    .totalNum(totalQuantity.get())
                    .realName(user.getNickname())
                    .userPhone(user.getPhone())
                    .userAddress(user.getAddress())
                    .payType(req.getPayType())
                    .shippingType(req.getShippingType())
                    .build();
            transactionTemplate.executeWithoutResult(transactionStatus -> {
                // 保存订单信息
                int inserted = orderMapper.insert(order);
                if (inserted < 1) {
                    throw new exception("创建订单失败");
                }
                List<BatchResult> batchResult = orderInfoMapper.insert(orderInfos);
                // 移除预下单订单
                CacheUtil.remove(preOrderNo);
                // 设置订单超时 三十分钟
            });
            return new CreateOrderResp(order.getId());
        } finally {
            lock.unlock();
        }
    }

    @Override
    public OrderPrepayResp prepayOrder(PrePayOrderReq req) {
        LambdaQueryWrapper<StoreOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StoreOrder::getId, req.getOrderId())
                .eq(StoreOrder::getUid, StpUtil.getLoginIdAsLong())
                .eq(StoreOrder::getPaid, PayStatus.UNPAID.getCode());
        StoreOrder order = getOne(queryWrapper);
        if (order == null) {
            throw new exception("订单不存在或已支付");
        }
        if (!order.getCreateTime().plusMinutes(30).isAfter(LocalDateTime.now())) {
            throw new exception("订单已过期");
        }
        LambdaUpdateWrapper<StoreOrder> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(StoreOrder::getId, order.getId())
                .eq(StoreOrder::getUid, order.getUid())
                .eq(StoreOrder::getPaid, PayStatus.UNPAID.getCode())
                .set(StoreOrder::getPaid, PayStatus.PAID.getCode());
        updateWrapper.set(StoreOrder::getPayTime, LocalDateTime.now());
        int updated = orderMapper.update(updateWrapper);
        if (updated < 1) {
            throw new exception("订单支付失败");
        }
        return OrderPrepayResp.builder()
                .orderId(order.getId())
                .orderNo(order.getOrderNo())
                .build();
    }

    @Override
    public PageResult<UsrOrderPageResp> orderList(UsrOrderPageReq req) {
        Page<UsrOrderPageResp> page = new Page<>(req.getPage(), req.getSize());
        IPage<UsrOrderPageResp> iPage = orderMapper.usrPage(page, req, StpUtil.getLoginIdAsLong());
        Map<String, UsrOrderPageResp> pageRespMap = iPage.getRecords().stream()
                .peek(e -> {
                    e.setProducts(new ArrayList<>());
                })
                .collect(Collectors.toMap(UsrOrderPageResp::getOrderNo, e -> e));
        if (pageRespMap.isEmpty()) {
            return PageResult.empty();
        }
        LambdaQueryWrapper<StoreOrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(StoreOrderInfo::getOrderNo, pageRespMap.keySet());
        List<StoreOrderInfo> orderInfos = orderInfoMapper.selectList(queryWrapper);
        for (StoreOrderInfo orderInfo : orderInfos) {
            UsrOrderPageResp pageResp = pageRespMap.get(orderInfo.getOrderNo());
            if (pageResp == null) {
                throw new exception("订单信息异常");
            }
            UsrOrderPageProductResp productResp = BeanUtil.toBean(orderInfo, UsrOrderPageProductResp.class);
            pageResp.getProducts().add(productResp);
        }
        return PageResult.of(iPage.getTotal(), iPage.getCurrent(), iPage.getSize(), pageRespMap.values());
    }

    @Override
    public boolean isVip(Long userId) {
        QueryWrapper<StoreOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", userId)
                .eq("paid", 1);
        queryWrapper.groupBy("uid");
        queryWrapper.select("IFNULL(sum(pay_price),0) as total");
        Map<String, Object> map = this.getMap(queryWrapper);
        return map != null && map.get("total") != null && new BigDecimal(map.get("total").toString()).compareTo(BigDecimal.valueOf(100)) >= 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderPrepayResp payOrder(PreOrderReq req) {
        // 如果判断下单方式是立即购买 还是购物车 选择不同的处理逻辑
        String orderType = req.getPreOrderType();
        long userId = StpUtil.getLoginIdAsLong();
        List<PreOrderDetail> orderDetails = req.getOrderDetails();
        if (orderDetails == null || orderDetails.isEmpty()) {
            throw new exception("订单详情不能为空");
        }
        Map<Long, Integer> productQuantity = new HashMap<>();
        switch (orderType) {
            // 立即购买必定只有一个商品
            case PreOrderType.buyNow -> {
                PreOrderDetail orderDetail = orderDetails.get(0);
                if (orderDetail.getQuantity() <= 0) {
                    throw new exception("数量必须大于0");
                }
                productQuantity.put(orderDetail.getProductId(), orderDetail.getQuantity());
            }
            // 购物车下单 可能有多个商品
            case PreOrderType.shoppingCart -> {
                // 获取这些商品的购物车id
                List<Long> cartIds = orderDetails.stream().map(PreOrderDetail::getShoppingCartId).toList();
                LambdaQueryWrapper<StoreCart> cartQuery = new LambdaQueryWrapper<>();
                cartQuery.in(StoreCart::getId, cartIds)
                        .eq(StoreCart::getUid, userId);
                List<StoreCart> storeCarts = storeCartMapper.selectList(cartQuery);
                if (storeCarts.isEmpty() || storeCarts.size() != cartIds.size()) {
                    throw new exception("购物车商品不存在");
                }
                for (StoreCart cart : storeCarts) {
                    productQuantity.put(cart.getProductId(), cart.getProductNum());
                }
            }
            default -> throw new exception("未知的预购类型");
        }
        // 获取这些商品的详细信息
        LambdaQueryWrapper<StoreProduct> productQuery = new LambdaQueryWrapper<>();
        productQuery.in(StoreProduct::getId, productQuantity.keySet())
                .eq(StoreProduct::getIsShow, 1);
        List<StoreProduct> products = productMapper.selectList(productQuery);
        if (products.isEmpty()) {
            throw new exception(ErrorMsg.PRODUCT_NOT_EXIST);
        }
        // 获取预下单页面商品信息
        List<ProductInfo> productInfos = products.stream().map(e -> {
            Integer quantity = productQuantity.get(e.getId());
            if (quantity == null) {
                throw new exception("商品信息错误");
            }
            // 计算单件商品价格
            return ProductInfo.builder()
                    .productId(e.getId())
                    .productImg(e.getImage())
                    .productName(e.getStoreName())
                    .quantity(quantity)
                    .price(e.getPrice())
                    .vipPrice(DecimalUtil.vip(e.getPrice()))
                    .build();
        }).toList();
        Boolean isVip = (Boolean) StpUtil.getExtra("isVip");
        // 原价
        AtomicReference<BigDecimal> totalPrice = new AtomicReference<>(BigDecimal.ZERO);
        // 现价
        AtomicReference<BigDecimal> actualPrice = new AtomicReference<>(BigDecimal.ZERO);
        for (ProductInfo info : productInfos) {
            totalPrice.set(totalPrice.get().add(DecimalUtil.multiply(info.getPrice(), info.getQuantity())));
            actualPrice.set(actualPrice.get().add(DecimalUtil.multiply(isVip ? info.getVipPrice() : info.getPrice(),
                    info.getQuantity())));
        }
        // 获取预购订单信息
        String orderNo = CommonUtil.getRandomStr();
        // 获取预下单商品信息
        AtomicInteger totalQuantity = new AtomicInteger(0);
        // 保存单个商品详细信息
        List<StoreOrderInfo> orderInfos = new ArrayList<>();
        for (ProductInfo info : productInfos) {
            totalQuantity.set(totalQuantity.get() + info.getQuantity());
            StoreOrderInfo orderInfo = StoreOrderInfo.builder()
                    .orderNo(orderNo)
                    .productId(info.getProductId())
                    .productName(info.getProductName())
                    .image(info.getProductImg())
                    .price(info.getPrice())
                    .payNum(info.getQuantity())
                    .build();
            orderInfos.add(orderInfo);
        }
        UserEntity user = userMapper.selectById(userId);
        if (user == null){
            throw new exception("用户不存在");
        }
        if (StringUtils.isBlank(user.getAddress())) {
            throw new exception("请先设置收货地址");
        }
        if (StringUtils.isBlank(user.getPhone())) {
            throw new exception("请先绑定手机号");
        }
        if (StringUtils.isBlank(user.getNickname())) {
            throw new exception("请先设置昵称");
        }
        // 创建父订单
        StoreOrder order = StoreOrder.builder()
                .uid(userId)
                .orderNo(orderNo)
                .proTotalPrice(totalPrice.get())
                .totalPrice(totalPrice.get())
                .payPrice(actualPrice.get())
                .totalNum(totalQuantity.get())
                .realName(user.getNickname())
                .userPhone(user.getPhone())
                .userAddress(user.getAddress())
                .payType("wechat")
                .shippingType(1)
                .build();
        transactionTemplate.executeWithoutResult(transactionStatus -> {
            // 保存订单信息
            int inserted = orderMapper.insert(order);
            if (inserted < 1) {
                throw new exception("创建订单失败");
            }
            List<BatchResult> batchResult = orderInfoMapper.insert(orderInfos);
        });
        LambdaUpdateWrapper<StoreOrder> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(StoreOrder::getId, order.getId())
                .eq(StoreOrder::getUid, order.getUid())
                .eq(StoreOrder::getPaid, PayStatus.UNPAID.getCode())
                .set(StoreOrder::getPaid, PayStatus.PAID.getCode());
        updateWrapper.set(StoreOrder::getPayTime, LocalDateTime.now());
        int updated = orderMapper.update(updateWrapper);
        if (updated < 1) {
            throw new exception("订单支付失败");
        }
        if (orderType.equals(PreOrderType.shoppingCart)) {
            // 购物车下单完成后删除购物车对应商品
            LambdaQueryWrapper<StoreCart> cartQuery = new LambdaQueryWrapper<>();
            cartQuery.in(StoreCart::getId, orderDetails.stream().map(PreOrderDetail::getShoppingCartId).toList());
            int delete = storeCartMapper.delete(cartQuery);
            if (delete < 1) {
                throw new exception("删除购物车商品失败");
            }
        }
        return OrderPrepayResp.builder()
                .orderId(order.getId())
                .orderNo(order.getOrderNo())
                .build();
    }
}




